
#pragma once

/** Helper macro for creating template types form values */
#define _t(v)   std::integral_constant<decltype(v), v>

namespace atr {
namespace ext {

/**
 * @brief   Namespace only for internal use of atr library
 * @details Please don't use this namespace outside the atr library.
 *          IF you REALLY need to - do it with special care.
 */
namespace __ {

    /**
     * @brief   Helper base class for instantiation
     * @details This is private class helper. This class connects output type
     *          with input types sequence(s).
     */
    template <class ... Type> struct __initialize;

    /**
     * @brief   Helper base class for instantiation - case with no input
     * @details This partial specialization is used when we don't have input
     *          types sequence. It produces empty object using default c-tor.
     *
     * @tparam  OutType is runtime type that has to be instantiated,
     *          e.g. std::list<int>, std::string
     */
    template <class OutType>
    struct __initialize<OutType>
    {
        /**
         * @brief   Runtime function that actually does the instantiation
         * @details This is the actual runtime logic that takes given
         *          compiletime container (e.g. atr::list<...>) and initialize
         *          object with given values
         *
         * @return  Instantieted object of type 'OutType' containing sequence of
         *          objects or sequence of pairs.
         *
         * @todo    Make it possible to provide custom function to dereference
         *          value from type (for now it's hardcoded
         *          @code::value@endcode)
         */
        static constexpr OutType with()
        {
            return OutType();
        }
    };

    /**
     * @brief   Helper base class for instantiation - case with one input
     *          sequence
     * @details This partial specialization is used when we have one input types
     *          sequence, e.h. for list or vector
     *
     * @tparam  OutType is runtime type that has to be instantiated,
     *          e.g. std::list<int>
     * @tparam  template <class ...> Base is input template compiletime
     *          container, e.g. atr::list
     * @tparam  class ... InTypes are types sequence contained in Base.
     *          From them we can dereference values.
     *
     * @todo    Make it possible to provide custom function to dereference
     *          value from type (for now it's hardcoded @code::value@endcode )
     */
    template <class OutType, template <class ...> class Base, class ... InTypes>
    struct __initialize<OutType, Base<InTypes...>>
    {
        /**
         * @brief   Runtime function that actually does the instantiation
         * @details This is the actual runtime logic that takes given
         *          compiletime container (e.g. atr::list<...>) and initialize
         *          object with given values
         *
         * @return  Instantieted object of type 'OutType' containing sequence of
         *          objects or sequence of pairs.
         *
         * @todo    Make it possible to provide custom function to dereference
         *          value from type (for now it's hardcoded
         *          @code::value@endcode)
         */
        static constexpr OutType with()
        {
            return { InTypes::value... };
        }
    };

    /**
     * @brief   Helper base class for instantiation - case with two input
     *          sequences
     * @details This partial specialization is used when we have two input types
     *          sequences, e.h. for map or set
     *
     * @tparam  OutType is runtime type that has to be instantiated,
     *          e.g. std::map<int, char>
     * @tparam  template <class ...> Base is input template compiletime
     *          container, e.g. atr::list
     * @tparam  class ... FirstInTypes are types sequence contained in Base.
     *          From them we can dereference values used as keys in output
     *          continer.
     * @tparam  class ... SecondInTypes are types sequence contained in Base.
     *          From them we can dereference values used as values in output
     *          continer.
     *
     * @todo    Make it possible to provide custom function to dereference value
     *          from type (for now it's hardcoded @code::value@endcode )
     */
    template < class OutType, template <class ...> class Base
             , class ... FirstInTypes, class ... SecondInTypes >
    struct __initialize<OutType, Base<FirstInTypes...>, Base<SecondInTypes...>>
    {
        /**
         * @brief   Runtime function that actually does the instantiation
         * @details This is the actual runtime logic that takes given
         *          compiletime container (e.g. atr::list<...>) and initialize
         *          object with given values
         *
         * @return  Instantieted object of type 'OutType' containing sequence of
         *          objects or sequence of pairs.
         *
         * @todo    Make it possible to provide custom function to dereference
         *          value from type (for now it's hardcoded
         *          @code::value@endcode)
         */
        static constexpr OutType with()
        {
            return { { FirstInTypes::value, SecondInTypes::value } ... };
        }
    };

} // namespace __

/**
 * @brief   Base class for instantiation
 * @details This is the bridge between type and object. This class was created
 *          as extension for atr library, that sequence created with atr
 *          operations on container could be used to instantiate container
 *          (e.g. std::list).
 *
 * @tparam  OutType Object type that we want to initialize, e.g. std::list<int>
 *          or std::map<int, char>
 */
template <class OutType>
struct initialize
{
    /**
     * @brief   Runtime function that actually does the instantiation
     * @details This is the actual runtime logic that takes given compiletime
     *          container (e.g. atr::list<...>) and initialize object with
     *          given values
     *
     * @tparam  class ... InTypes types used to instantiate object. For now we
     *          have specialization obly for one (makes containers like 'list',
     *          'vector') or two (makes containers like 'map', 'set') types.
     * @return  Instantieted object of type 'OutType' containing sequence of
     *          objects or sequence of pairs.
     *
     * @todo    Make it possible to provide custom function to dereference value
     *          from type (for now it's hardcoded @code::value@endcode )
     */
    template <class ... InTypes>
    static constexpr OutType with()
    {
        return __::__initialize<OutType, InTypes...>::with();
    }
};

} // namespace ext
} // namespace atr
